Loop At ITAB Group By
Mühsam ernährt sich das Eichhörnchen. Die heutige Nuss galt dem Befehlszusatz GROUP BY für den LOOP über eine interne Tabelle. Es gibt dankenswerter Weise in der SAP-Doku inzwischen viele Beispiele. Diese sind jedoch sehr abstrakt. Sie zeigen die Syntax, verdeutlichen aber nicht unbedingt, was damit möglich ist.
Ich präsentiere euch heute ein paar Möglichkeiten der Gruppierung, die hoffentlich die Funktionsweise deutlich machen.
Beispieldaten
im Folgenden verwende ich diese Struktur für meine Beispiele:
TYPES: BEGIN OF _doc,
docnr TYPE n length 10,
itmno TYPE n length 6,
category type c length 2,
del_flag type abap_bool,
END OF _doc,
_docs TYPE SORTED TABLE OF _doc WITH UNIQUE KEY docnr itmno.
DATA(documents) = VALUE _docs(
( docnr = 12001 itmno = 10 category = 'A1' del_flag = 'X' )
( docnr = 12001 itmno = 20 category = 'A1' del_flag = ' ' )
( docnr = 12002 itmno = 10 category = 'A2' del_flag = 'X' )
( docnr = 12003 itmno = 10 category = 'B3' del_flag = ' ' )
( docnr = 12003 itmno = 20 category = 'B1' del_flag = ' ' ) ).
Die Tabelle soll generelle Belege mit Positionsnummer, einem Positionstypen und einem Löschkennzeichen simulieren.
Generelle Funktionsweise
Der Befehlszusatz GROUP BY zum LOOP bietet die Möglichkeit der Gruppierung, ähnlich wie die Sprachelemente AT NEW oder AT CHANGE OF innerhalb eines LOOP. Allerdings bietet der GROUP BY Befehl noch einige weitere Möglichkeiten.
In der einfachen Variante kannst du ein Feld der internen Tabelle angeben, nach dem gruppiert werden soll.
LOOP AT documents INTO DATA(document)
GROUP BY document-docnr INTO DATA(docgrp).
WRITE: / docgrp.
ENDLOOP.
Das Ergebnis ist eine Liste aller eindeutigen Belegnummern:
0000012001
0000012002
0000012003
Gruppenelemente
Es ist nun eine Gruppe DOCGRP vorhanden. Auf die Zeilen dieser Gruppe kann mit Hilfe des Befehls LOOP AT GROUP zugegriffen werden:
LOOP AT GROUP docgrp INTO DATA(docline).
WRITE: / doc1line-docnr, docline-itmno.
ENDLOOP.
So weit so gut…
Es gibt zwei Interessante Zusätze in einer Gruppe:
- GROUP INDEX
- GROUP SIZE
Diese können zusätzlich definiert werden. Im LOOP kann darauf zugegriffen werden.
GROUP INDEX
Der Zusatz GRIOUP INDEX liefert den aktuellen Index der (unsortierten) Gruppe.
GROUP SIZE
Der Zusatz GROUP SIZE liefert Informationen über die Anzahl der enthaltenen Gruppenelemente.
Beispiel
LOOP AT documents INTO DATA(document)
GROUP BY
( doc = document-docnr
size = GROUP SIZE
index = GROUP INDEX )
INTO DATA(docgrp).
WRITE: / |elements in group {
docgrp-index align = left } "{
docgrp-doc }": {
docgrp-size ALIGN = left } entries|.
LOOP AT GROUP docgrp INTO DATA(doc).
WRITE: / doc-docnr, doc-itmno.
ENDLOOP.
ENDLOOP.
Ergebnis:
number of elements in group 1: 2 entries
0000012001 000010
elements in group 1 "0000012001": 2 entries
0000012001 000010
0000012001 000020
elements in group 2 "0000012002": 1 entries
0000012002 000010
elements in group 3 "0000012003": 2 entries
0000012003 000010
0000012003 000020
In diesem Fall müssen die Gruppenfelder, die nun in Klammern eingefasst werden müssen, selbst definiert werden.
Dynamische Gruppen
Jetzt kommt der spannende Teil, der einen großen Vorteil gegenüber der alten AT NEW Gruppenstufenverarbeitung hat. Die Gruppen können dynamisch anhand der Feldwerte definiert werden. Im Folgenden Beispiel fasse ich alle Einträge mit einem Löschkennzeichen in der Gruppe “deleted” zusammen. Alle anderen Einträge kommen in die Gruppe “valid”.
LOOP AT documents
INTO DATA(doc)
GROUP BY (
del_group = COND string( WHEN doc-del_flag = space THEN 'valid' ELSE 'deleted' )
size = GROUP SIZE
index = GROUP INDEX )
INTO DATA(docgrp).
WRITE: / 'number of', docgrp-del_group, 'entries:', docgrp-size.
ENDLOOP.
Ergebnis:
number of deleted entries: 2
number of valid entries: 3
Ich füge also je nachdem, ob das Löschhkennzeichen gesetzt ist oder nicht, eine andere Gruppenbezeichnung ein. Das ist besonders dann interessant, wenn man mehrere unterschiedliche Elemente gruppieren möchte. Beispielsweise alle Positionstypen, die mit A oder B beginnen:
LOOP AT documents
INTO DATA(doc)
GROUP BY (
cat = doc-category(1)
size = GROUP SIZE
index = GROUP INDEX )
INTO DATA(docgrp).
WRITE: / 'number of items in category',
docgrp-cat LEFT-JUSTIFIED NO-GAP,
docgrp-size.
ENDLOOP.
Ergebnis:
number of items in category A 3
number of items in category B 2
Ebenso könnte man nach Bestellungen gruppieren, die einen “geringen” oder einen “höheren” Bestellwert haben. Oder ich kann Aufträge direkt nach Aufträgen mit A-, B- oder C-Kunden gruppieren, indem ich für die Ermittlung der Kundenklassifizierung eine Methode in der GROUP BY Klausel verwende.
Gruppen gruppieren
Die definierten Gruppen können ebenfalls weiter gruppiert werden. Im folgenden Beispiel gruppiere ich erst nach der Positionstypengruppe (A oder B) aus dem obigen Beispiel. In dieser Gruppe gruppiere ich dann noch einmal nach dem eigentlichem Positionstyp (A1, A2, …). zudem berücksichtige ich in der WHERE-Bedingung nur die Positionen ohne Löschkennzeichen. Für dieses Beispiel habe ich die Datenbasis etwas erweitert…
LOOP AT documents
INTO DATA(doc2)
GROUP BY (
cat = doc2-category(1)
size = GROUP SIZE
index = GROUP INDEX )
INTO DATA(docgrp2).
WRITE: /1 'number of items in category',
docgrp2-cat LEFT-JUSTIFIED NO-GAP,
docgrp2-size.
LOOP AT GROUP docgrp2 INTO DATA(docline2)
WHERE del_flag = space GROUP BY ( category = docline2-category ) INTO DATA(grpcat).
write: /5 'category', grpcat-category.
LOOP AT GROUP grpcat INTO DATA(cat).
WRITE: /9 cat-docnr, cat-itmno, cat-category.
ENDLOOP.
ENDLOOP.
ENDLOOP.
Ergebnis:
number of items in category A 5
category A1
0000012001 000020 A1
0000012003 000030 A1
category A2
0000012004 000010 A2
number of items in category B 5
category B3
0000012003 000010 B3
0000012003 000020 B3
0000012005 000010 B3
0000012005 000020 B3
category B1
0000012006 000010 B1
Einschränkungen
Bei meinem heutigen Ausflug in die Gruppenstufen bin ich über folgende Einschränkungen gestolpert:
- Sortierung nur nach Gruppenstufenfeldern möglich, aber nicht nach GROUP SIZE
- Kein WHERE über Gruppenstufen möglich
Sortierung
Ich wollte gerne die Gruppen nach der Anzahl der Elemente sortieren. Das ist leider nicht möglich.
WHERE über Gruppenstufen
Es ist anscheinend nicht möglich, die erzeugten Gruppen direkt über eine WHERE-Bedingung einzuschränken. In den meisten Fällen kann man es sicherlich über eine geschickte WHERE-Bedingung über die Tabelle abbilden (zum Beispiel WHERE category(1) = ‘A’). Allerdings wäre eine Einschränkung über die Gruppen selbst eventuell auch hilfreich. Zum Beispiel könnte die Bedingung für die Gruppe DEL_GROUP, die ich mit valid und deleted definiert hatte, etwas komplizierter und aufgrund einen Methodenaufrufes nicht ersichtlich sein. Ich würde dann trotzdem nur über die Einträge mit valid verarbeiten wollen. Das geht natürlich mit CHECK innerhalb des LOOP, eine WHERE-Bedingung wäre jedoch eleganter.
- Interview mit Björn Schulz (Software-Heroes.com) - 3. September 2024
- Daten aus ALV ermitteln - 3. September 2024
- So lange es den SAPGUI noch gibt… - 27. Juni 2024